/**
 * auth: zhukaijun
 * date: 2021-4-13
 * des: api handler
 */
import ApiList from './ApiList.json';
import axios from "axios";
import request from "@/apis/request";
import { cloneLoop } from '@/utils/ObjectUtil';

class ApiInvoker {
    constructor(v, apiGroup, config) {
        this.v = v;
        this.apiGroup = apiGroup;
        this.config = config;
    }

    conver(data, index, final) {
        if (typeof data !== 'object') {
            data = {};
        }
        data.index = index;
        data.final = final;
        return data;
    }

    get apiList() {
        return ApiList[this.apiGroup];
    }

    set format(call) {
        if (typeof call != 'function') {
            return;
        }
        this.conver = call;
    }

    objectCopy(...os) {
        let nO = {},
            rms = [];
        for (let o of os) {
            for (let z in o) {
                if (o[z] === '#rm') {
                    rms.push([z]);
                    continue;
                }
                nO[z] = o[z];
            }
        }
        for (let rm in rms) {
            delete nO[rm];
        }
        return nO;
    }


    invoker(name, param, postBody, method, config) {
        let self = this;
        return {
            method: method,
            postBody: postBody,
            config: config,
            lastParam: {},
            // then() {
            //
            // },
            executeArray(cb, method, syncParam, config) {
                let transformObjToArr = (data, isRoot = true) => {
                    if (!data || typeof data !== 'object') return data;
                    let dataNew = [];
                    let keys = Object.keys(data);
                    if (keys.includes('0')) {
                        keys.forEach(k => dataNew[k] = transformObjToArr(data[k], false));
                    } else {
                        keys.forEach(k => data[k] = transformObjToArr(data[k], false));
                    }
                    if (isRoot) {
                        return dataNew.length ? JSON.stringify(dataNew) : JSON.stringify(data);
                    } else {
                        return dataNew.length ? dataNew : data;
                    }
                }
                let that = this,
                    np = null;
                if (ApiList[self.apiGroup][name].config) {
                    np = param || syncParam;
                } else if (syncParam) {
                    np = self.objectCopy(ApiList[self.apiGroup][name].defaultParam, param, syncParam);
                } else if (this.lastParam[name]) {
                    np = this.lastParam[name];
                } else {
                    np = self.objectCopy(ApiList[self.apiGroup][name].defaultParam, param);
                }
                let req = axios.create({
                    method: "POST",
                    headers: {
                        'Content-Type': 'application/json;',
                        'token': window.localStorage.getItem('token')
                    },
                    transformRequest: [
                        (data) => {
                            return transformObjToArr(data)
                        }
                    ]
                });
                return req({ url: ApiList[self.apiGroup][name].url, data: np, param: np }).then(resp => {
                    cb && cb(self.conver(resp));
                    that.lastParam[name] = np;
                });
            },
            execute(cb, method, syncParam, config, index, final) {
                let that = this,
                    np = null;
                if (ApiList[self.apiGroup][name].config) {
                    np = param || syncParam;
                } else if (syncParam) {
                    np = self.objectCopy(ApiList[self.apiGroup][name].defaultParam, param, syncParam);
                } else if (this.lastParam[name]) {
                    np = this.lastParam[name];
                } else {
                    np = self.objectCopy(ApiList[self.apiGroup][name].defaultParam, param);
                }
                let addUrl = '';
                if (np.urlParam) {
                    addUrl = '/' + np.urlParam;
                    np = null;
                }
                if ((this.method || method) === 'get') {
                    request({
                        url: ApiList[self.apiGroup][name].url,
                        method: (this.method || method),
                        ...np
                    }).then(resp => {
                        cb && cb(self.conver(resp), index, final);
                        that.lastParam[name] = np;
                    });
                    return that;
                }
                return new Promise((resolve, reject) => {
                    axios.post(ApiList[self.apiGroup][name].url + addUrl, np, (target => {
                            let ss = axios.CancelToken.source();
                            config = config || {};
                            config.cancelToken = ss.token;
                            // config.headers = {
                            //     'token': window.localStorage.getItem('token')
                            // }
                            target.cancelTask = {};
                            target.cancelTask[index] = config.cancelToken;
                            return config;
                        })(self))
                        .then(response => {
                            resolve(response.data);
                        })
                        .catch(err => {
                            reject(err);
                        });
                }).then(resp => {
                    cb && cb(self.conver(resp), index, final);
                    that.lastParam[name] = np;
                }).catch(err => {});
            }
        }
    }

    // eslint-disable-next-line no-empty
    invokerList(list, parallel) {
        let self = this,
            finalResult = {
                then() {

                }
            };
        if (typeof list !== 'object') {
            return;
        }
        let invokerList = [];
        for (let task of list) {
            invokerList.push(this.invoker(task.name, task.param || {}, task.postBody));
        }
        return parallel ? {
            execute(cb, syncParam) {
                let taskSize = invokerList.length,
                    index = 0,
                    result = [],
                    count = 0;
                for (let vk of invokerList) {
                    let ccb = (data, index, done) => {
                            cb && cb(data, index, done);
                            result.push(data);
                        },
                        config = cloneLoop(self.config);
                    config.onUploadProgress = evt => {
                        self.config.status(config.exeIndex, evt);
                    }
                    config.exeIndex = index;
                    vk.execute(ccb, vk.method, syncParam, vk.postBody || config, index++, taskSize === index).then(ok => {
                        count++;
                        if (count >= invokerList.length) {
                            finalResult.then(ok);
                        }
                    });
                }
                return finalResult;
            }
        } : {
            async execute(cb, syncParam) {
                let taskSize = invokerList.length,
                    index = 0,
                    result = [];
                for (let vk of invokerList) {
                    let ccb = (data, index, done) => {
                        cb && cb(data, index, done);
                        result.push(data);
                    }
                    if (self.config && self.config.status) {
                        self.config.onUploadProgress = evt => {
                            self.config.status(index - 1, evt);
                        }
                    }
                    await vk.execute(ccb, vk.method, syncParam, vk.postBody || self.config, index++, taskSize === index);
                }
                return Promise.resolve(result);
            }
        }

    }
}

let instance = 0;

export default {
    newInstance(v, create, apiGroup, config, cover) {
        if (cover || !instance) {
            instance = new ApiInvoker(v, apiGroup, config);
        }
        if (create) {
            return new ApiInvoker(v, apiGroup, config);
        }
        return instance;
    },

    nativeGet(url, cb, error) {
        let xhr = new XMLHttpRequest();
        xhr.open("get", url, true);
        xhr.responseType = "blob";
        xhr.onload = () => {
            if (this.status == 200) {
                const reader = new FileReader()
                reader.onload = () => {
                    cb(reader.result);
                }
                reader.readAsText(this.response);
            }
        };
        xhr.onerror = (e) => {
            error(e);
        }
        xhr.send();
    }
}